/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.geoboard.tools;

import java.io.IOException;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import android.util.Log;

import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.HttpHeaderParser;

/**
 * A request for retrieving a T type response body at a given URL that also
 * optionally sends along a JSON body in the request specified.
 *
 * @param <T> JSON type of response expected
 */
public class JacksonRequest<T> extends Request<T> {
    /** Charset for request. */
    private static final String PROTOCOL_CHARSET = "utf-8";

    /** Content type for request. */
    private static final String PROTOCOL_CONTENT_TYPE =
        String.format("application/json; charset=%s", PROTOCOL_CHARSET);

    private String TAG = this.getClass().getSimpleName();
    private final Listener<T> mListener;
    private final ObjectMapper mMapper;
    private final T mRequestObject;
    private final TypeReference<T> mRequestType;
    private String mCacheKey;

    public JacksonRequest(int method, String url, T requestObject, TypeReference<T> requestType,
    		Listener<T> listener, ErrorListener errorListener) {
        super(method, url, errorListener);
        mRequestObject = requestObject;
        mRequestType = requestType;
        mListener = listener;
        mMapper = new ObjectMapper();
    }

    @Override
    protected void deliverResponse(T response) {
        mListener.onResponse(response);
    }

    
   /*  	
    * @param response Response from the network
    * @return The parsed response, or sent object in case of post
    */
	@SuppressWarnings("unchecked")
	@Override
    protected Response<T> parseNetworkResponse(NetworkResponse response) {
		String jsonString = null;
		try {
			jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
			if(jsonString == null || jsonString.equals("")) {
				return (Response<T>) Response.success(mRequestObject,
	                    HttpHeaderParser.parseCacheHeaders(response));
			} else {	
	    		VolleyLog.d("reading jsonString: ", jsonString);
	            return (Response<T>) Response.success(mMapper.readValue(jsonString, mRequestType),
	                    HttpHeaderParser.parseCacheHeaders(response));
			}
        } catch (JsonParseException e) {
        	VolleyLog.e(e, "Unable to parse %s from Json", jsonString);
        	return Response.error(new ParseError(e));        	
		} catch (JsonMappingException e) {
			VolleyLog.e(e, "Unable to map %s from Json", jsonString);
			return Response.error(new ParseError(e));
		} catch (IOException e) {
			VolleyLog.e(e, "IO Exception on mapping %s from Json", jsonString);
			return Response.error(new ParseError(e));
		}
    }

    @Override
    public String getBodyContentType() {
        return PROTOCOL_CONTENT_TYPE;
    }

    /*
     * encode parameters and body
     */
    @Override
    public byte[] getBody() {
        //convert object to json and encode
    	if (mRequestObject != null) {
			try {
				Log.d(TAG, "writing json " + mMapper.writeValueAsString(mRequestObject));
				return mMapper.writeValueAsString(mRequestObject).getBytes(PROTOCOL_CHARSET);
			} catch (JsonGenerationException e) {
				VolleyLog.e(e, "Unable to generate Json from %s", mRequestObject.getClass().getSimpleName());
				return null;
			} catch (JsonMappingException e) {
				VolleyLog.e(e, "Unable to map %s to Json", mRequestObject.getClass().getSimpleName());
				return null;
			} catch (IOException e) {
				VolleyLog.e(e, "IO Exception on mapping %s to Json", mRequestObject.getClass().getSimpleName());
				return null;
	        }
    	}
    	Log.d(TAG, "null requestObject");
		return null;
    }
    
    @Override
    public String getCacheKey() {
    	if (mCacheKey == null)
    		return getUrl();
    	else
    		return mCacheKey;
    }
    
    public void setCacheKey(String cacheKey) {
    	mCacheKey = cacheKey;
    }
    
}
